﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Linq;
using System.Text;
//using Gma.UserActivityMonitor;

namespace MercuryFE
{
    /// <summary>
    /// Takes care of key handler, and key mappings for both navigation and games.
    /// </summary>
    public class NavigationHook
    {

        #region enums
        public enum NavigationType
        {
            Standard, Game
        };

        public enum NavigationTask
        {
            Left, Right, Up, Down, Select, Back, Info, None
        };
        #endregion

        #region properties

        private IntPtr _processWindowHandle = IntPtr.Zero;

        public IntPtr ProcessWindowHandle { get { return _processWindowHandle; } set { _processWindowHandle = value; } }

        //private Dictionary<string, string> _navigationKeyMappings = null;
       // private Dictionary<string, string> _gameKeyMappings = null;
        private bool _isInitialised = false;

        private Inputs _inputs = null;

        public bool IsInitialised
        {
            get { return _isInitialised; }
        }
        public NavigationType CurrentNavigationType {
            get; set;
        }
 
       // private Dictionary<string, string> _keyCodeLookup = null;


        private NavigationHook.NavigationTask _previousNaviTask = NavigationHook.NavigationTask.None;
        private int _previousNaviTaskCount = 0;
        private readonly int _naviLoopThreshold = 15; // number of loops, while navigation 'held' to trigger again - eg, holding 'down'
        private readonly int _naviLoopFastThreshold = 4; // number of navi loop thresholds reached before moving faster through selections..

        #endregion


        public void InitialiseInputs(IntPtr windowHandle)
        {
            bool useMouse = false, useKeyboard = false, useJoystick = false;
            ClientLib.getControlFlags(ref useKeyboard, ref useMouse, ref  useJoystick);

            Dictionary<string, InputDeviceTypeAction> keyboardMappings = null, mouseMappings = null, joystickMappings = null;

            if(useKeyboard)
                keyboardMappings = ClientLib.getGUIKeyMappings(InputDeviceType.keyboard);

            if(useMouse)
                mouseMappings = ClientLib.getGUIKeyMappings(InputDeviceType.mouse);

            if (useJoystick)
                joystickMappings = ClientLib.getGUIKeyMappings(InputDeviceType.joystick);

            _inputs = new Inputs(windowHandle, useKeyboard, useMouse, useJoystick, keyboardMappings, mouseMappings, joystickMappings);
        }

        //public void InitialiseInputs(IntPtr windowHandle, bool keyboard, bool mouse, bool joystick) {

        //    // need to load mappings
        //    Dictionary<string, InputDeviceTypeAction> mouseMappings = new Dictionary<string, InputDeviceTypeAction>
        //        {
        //              {"0", InputDeviceTypeAction.Select }
        //            , {"1", InputDeviceTypeAction.Back }
        //            , {"2", InputDeviceTypeAction.Info }
        //        };


        //    Dictionary<string, InputDeviceTypeAction> keyboardMappings = ClientLib.getGUIKeyMappings(InputDeviceType.keyboard);

        //    _inputs = new Inputs(windowHandle, keyboard, mouse, joystick, keyboardMappings, mouseMappings, null);
        //}
         // checks for all devices...



        public void PollDevices(bool hasVerticalMenu, bool hasHorizontalMenu, ref NavigationTask naviTask, ref bool normalSpeed)
        {
            List<InputDeviceState> states = _inputs.pollDevices();
            naviTask = NavigationHook.NavigationTask.None;
            bool doNavigation = false;
            foreach (InputDeviceState state in states)
            {

                if (state.HasValue())
                {
                    doNavigation = true;

                    // if vertical orientated menu, up/down preference over left/right (don't screw up analog inputs like joystick and mouse)
                    if (hasVerticalMenu)
                    {
                        if (state.Up) { naviTask = NavigationHook.NavigationTask.Up; break; }
                        if (state.Down) { naviTask = NavigationHook.NavigationTask.Down; break; }
                        if (state.Left) { naviTask = NavigationHook.NavigationTask.Left; break; }
                        if (state.Right) { naviTask = NavigationHook.NavigationTask.Right; break; }

                    }

                    if(hasHorizontalMenu)
                    {
                        if (state.Left) { naviTask = NavigationHook.NavigationTask.Left; break; }
                        if (state.Right) { naviTask = NavigationHook.NavigationTask.Right; break; }
                        if (state.Up) { naviTask = NavigationHook.NavigationTask.Up; break; }
                        if (state.Down) { naviTask = NavigationHook.NavigationTask.Down; break; }
                    }

                    if (state.Select) { naviTask = NavigationHook.NavigationTask.Select; break; }
                    if (state.Back) { naviTask = NavigationHook.NavigationTask.Back; break; }
                    if (state.Info) { naviTask = NavigationHook.NavigationTask.Info; break; }

                    break; // break after first device state handled
                }
            }

            // get active window
            if (doNavigation)
            {
                
                if (_previousNaviTask == naviTask)
                {
                    _previousNaviTaskCount++;
                }
                else
                {
                    _previousNaviTask = naviTask;
                    _previousNaviTaskCount = 0;
                }

                // only do navigation on the specified loop threshold, eg 30 x 16ms ~ 500ms
                int loopCheck = _previousNaviTaskCount % _naviLoopThreshold;
                if (loopCheck == 0)
                {
                    //normalSpeed = ((_previousNaviTaskCount / _naviLoopThreshold) >= _naviLoopFastThreshold) ? false : true;
                }
                else
                {
                    // we don't do anything for this loop - set to none
                    naviTask = NavigationTask.None;
                }
                
            }
            else
            {
                _previousNaviTask = NavigationHook.NavigationTask.None;
                _previousNaviTaskCount = 0;
            }


        }



        /*
        /// <summary>
        /// Sets up the global key handler, as well as loading in the key (actual task) mappings for standard navigation.
        /// </summary>
        /// <param name="handler"></param>
        public void setup(System.Windows.Forms.KeyEventHandler handler)
        {
            // make sure keymappings are setup
            this._isInitialised = true;

            this.CurrentNavigationType = NavigationType.Standard;

            if (_keyCodeLookup == null)
                initKeyCodeLookup();

            //HookManager.KeyDownRemoveAllHandlers();

            // remove previous mappings
            _navigationKeyMappings = new Dictionary<string, string>();

            foreach (KeyValuePair<string, string> mapping in ClientLib.getConfigs("Keys"))
                _navigationKeyMappings.Add(mapping.Value, mapping.Key);

             //HookManager.KeyDown += handler;
        }

        /// <summary>
        /// Reloads the game key mappings for the specified game, as well as setting the navigation type to standard
        /// </summary>
        /// <param name="gameId">The unique identifier for the game to have the key mappings loaded</param>
        public void setGameKeyMappings(int gameId)
        {
            CurrentNavigationType = NavigationType.Game;
            _gameKeyMappings = ClientLib.getKeyMappings((int)gameId);
        }

        */

        /*
        /// <summary>
        /// Returns the new key code to be used in either navigation or games (key remappings/ suppression)
        /// </summary>
        /// <param name="keyCode">The keycode which has just been pressed</param>
        /// <param name="isNavigate">Whether it is currently in standard (navigation) or game mode</param>
        /// <param name="doSuppress">Whether the key pressed should be suppressed, and in turn not passed onto application/ game</param>
        /// <returns>The new key code, if any, to be used to simulate a key press (for remapping)</returns>
        public string getnewKeyCode(System.Windows.Forms.Keys keyCode, ref bool isNavigate, ref bool doSuppress)
        {
            string newKeyCode = null;

            string keyCodeString = keyCode.ToString();

            if (CurrentNavigationType == NavigationType.Standard)
            {
                doSuppress = false; // allow other key mappings...
                isNavigate = true;
                if (_navigationKeyMappings.ContainsKey(keyCodeString))
                {
                    newKeyCode = _navigationKeyMappings[keyCodeString].ToString();
                    doSuppress = true; // supress key if its a valid navigation key
                    return newKeyCode;
                }
            }
            else if (CurrentNavigationType == NavigationType.Game)
            {

                doSuppress = false;
                isNavigate = false;

                if (_gameKeyMappings == null)
                    return null;

                if (_gameKeyMappings.ContainsKey(keyCodeString))
                {
                    doSuppress = true;
                    newKeyCode = _gameKeyMappings[keyCodeString].ToString();
                    if (newKeyCode == null)
                    {
                        return newKeyCode;
                    }
                    else if (_keyCodeLookup.ContainsKey(newKeyCode))
                    {
                        newKeyCode = _keyCodeLookup[newKeyCode];
                        return newKeyCode;
                    }
                    else
                    {
                        return newKeyCode;
                    }
                }
            }
  
            return null;
  



        }
         * 
         * */
        /*
        /// <summary>
        /// Loads the key lookup, from KeyCode to Virtual Key Code, used in windows key simulation (that is, remap to another key)
        /// </summary>
        private void initKeyCodeLookup()
        {
            // KEY REMAPPING

            string keyFile = ClientLib.getCurrentDir() + "\\DataFiles\\keys.xml";
            var KeyMapDoc = XElement.Load(keyFile);

            _keyCodeLookup = (from k in KeyMapDoc.Descendants("key")
                                 select new
                                 {
                                     code = (string)k.Attribute("code")
                                     ,
                                     virtualCode = (string)k.Attribute("virtualKey")
                                 }).ToDictionary(a => a.code, b => b.virtualCode);
        }
         * */
    }
}
